home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / fax / src / util / SendFaxClient.c++ < prev    next >
C/C++ Source or Header  |  1994-08-01  |  19KB  |  671 lines

  1. /*    $Header: /usr/people/sam/fax/util/RCS/SendFaxClient.c++,v 1.26 1994/03/09 18:46:28 sam Rel $ */
  2. /*
  3.  * Copyright (c) 1990, 1991, 1992, 1993, 1994 Sam Leffler
  4.  * Copyright (c) 1991, 1992, 1993, 1994 Silicon Graphics, Inc.
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software and 
  7.  * its documentation for any purpose is hereby granted without fee, provided
  8.  * that (i) the above copyright notices and this permission notice appear in
  9.  * all copies of the software and related documentation, and (ii) the names of
  10.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  11.  * publicity relating to the software without the specific, prior written
  12.  * permission of Sam Leffler and Silicon Graphics.
  13.  * 
  14.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  15.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  16.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  17.  * 
  18.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  19.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  20.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  22.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  23.  * OF THIS SOFTWARE.
  24.  */
  25. #include <stdlib.h>
  26. #include <stdarg.h>
  27. #include <ctype.h>
  28. #include <paths.h>
  29.  
  30. #include <osfcn.h>
  31. #include <unistd.h>
  32. #include <pwd.h>
  33. #include <sys/stat.h>
  34. #include <fcntl.h>
  35. #include <string.h>
  36. extern "C" {
  37. #include <sys/socket.h>
  38. #include <netinet/in.h>
  39. #include <netdb.h>        // XXX
  40. }
  41.  
  42. #include <Dispatch/dispatcher.h>
  43.  
  44. #include "SendFaxClient.h"
  45. #include "TypeRules.h"
  46. #include "DialRules.h"
  47. #include "PageSize.h"
  48. #include "config.h"
  49.  
  50. struct FileInfo : public fxObj {
  51.     fxStr    name;
  52.     const TypeRule* rule;
  53.     fxBool    isTemp;
  54.     fxBool    tagLine;
  55.  
  56.     FileInfo();
  57.     ~FileInfo();
  58. };
  59. fxDECLARE_ObjArray(FileInfoArray, FileInfo);
  60.  
  61.  
  62. const fxStr SendFaxClient::TypeRulesFile(FAX_TYPERULES);
  63.  
  64. SendFaxClient::SendFaxClient()
  65. {
  66.     typeRules = NULL;
  67.     dialRules = NULL;
  68.     files = new FileInfoArray;
  69.     pollCmd = FALSE;
  70.     coverSheet = TRUE;
  71.     gotPermission = FALSE;
  72.     permission = FALSE;
  73.     verbose = FALSE;
  74.     killtime = FAX_TIMEOUT;    // default time to kill the job
  75.     hres = 204;            // G3 standard
  76.     vres = FAX_DEFVRES;        // default resolution
  77.     pageWidth = 0;
  78.     pageLength = 0;
  79.     totalPages = 0;
  80.     maxRetries = -1;
  81.     notify = FAX_DEFNOTIFY;    // default notification
  82.     setup = FALSE;
  83. }
  84.  
  85. SendFaxClient::~SendFaxClient()
  86. {
  87.     u_int i;
  88.     for (i = 0; i < coverPages.length(); i++)
  89.     unlink((char*) coverPages[i]);
  90.     for (i = 0; i < tempFiles.length(); i++)
  91.     unlink((char*) tempFiles[i]);
  92.     delete typeRules;
  93.     delete dialRules;
  94.     delete files;
  95. }
  96.  
  97. fxBool
  98. SendFaxClient::prepareSubmission()
  99. {
  100.     u_int i, n;
  101.  
  102.     if (!setupSenderIdentity(from))
  103.     return (FALSE);
  104.     if (pageSize == "" && !setPageSize("default"))
  105.     return (FALSE);
  106.     typeRules = TypeRules::read(fxStr(FAX_LIBDATA) | "/" | TypeRulesFile);
  107.     if (!typeRules) {
  108.     printError("Unable to setup file typing and conversion rules");
  109.     return (FALSE);
  110.     }
  111.     typeRules->setVerbose(verbose);
  112.     dialRules = new DialStringRules(fxStr(FAX_LIBDATA) | "/" | FAX_DIALRULES);
  113.     dialRules->setVerbose(verbose);
  114.     if (!dialRules->parse() && verbose)        // NB: not fatal
  115.     printWarning("unable to setup dialstring rules");
  116.     for (i = 0, n = files->length(); i < n; i++)
  117.     if (!handleFile((*files)[i]))
  118.         return (FALSE);
  119.     /*
  120.      * Convert dialstrings to a displayable format.  This
  121.      * deals with problems like calling card access codes
  122.      * getting stuck on the cover sheet and/or displayed in
  123.      * status messages.
  124.      */
  125.     externalNumbers.resize(destNumbers.length());
  126.     for (i = 0, n = destNumbers.length(); i < n; i++)
  127.     externalNumbers[i] = dialRules->displayNumber(destNumbers[i]);
  128.     /*
  129.      * Suppress the cover page if we're just doing a poll;
  130.      * otherwise, generate a cover sheet for each destination
  131.      * (We do it now so that we can be sure everything is ready
  132.      * to send before we setup a connection to the server.)
  133.      */
  134.     if (pollCmd && files->length() == 0)
  135.     coverSheet = FALSE;
  136.     if (coverSheet) {
  137.     coverPages.resize(externalNumbers.length());
  138.     for (i = 0, n = externalNumbers.length(); i < n; i++)
  139.         coverPages[i] = makeCoverPage(destNames[i], externalNumbers[i],
  140.         destCompanys[i], destLocations[i], senderName);
  141.     }
  142.     return (setup = TRUE);
  143. }
  144.  
  145. #define    CHECK(x)    { if (!(x)) return (FALSE); }
  146.  
  147. fxBool
  148. SendFaxClient::submitJob()
  149. {
  150.     CHECK(setup && callServer())
  151.     startRunning();
  152.     /*
  153.      * Explicitly check for permission to submit a job
  154.      * before sending the input documents.  This way we
  155.      * we avoid sending a load of stuff just to find out
  156.      * that the user/host is not permitted to submit jobs.
  157.      */
  158.     CHECK(sendLine("checkPerm", "send"))
  159.     permission = gotPermission = FALSE;
  160.     while (isRunning() && !getPeerDied() && !gotPermission)
  161.     Dispatcher::instance().dispatch();
  162.     CHECK(permission)
  163.  
  164.     /*
  165.      * Transfer the document files first so that they
  166.      * can be referenced multiple times for different
  167.      * destinations.
  168.      */
  169.     for (u_int i = 0; i < files->length(); i++) {
  170.     const FileInfo& info = (*files)[i];
  171.     if (info.rule->getResult() == TypeRule::TIFF)
  172.         CHECK(sendData("tiff", info.name))
  173.     else
  174.         CHECK(sendData("postscript", info.name))
  175.     }
  176.     if (pollCmd)
  177.     CHECK(sendLine("poll", ""))
  178.     for (i = 0; i < destNumbers.length(); i++) {
  179.     CHECK(sendLine("begin", i))
  180.     if (sendtime != "")
  181.         CHECK(sendLine("sendAt", sendtime))
  182.     if (maxRetries >= 0)
  183.         CHECK(sendLine("maxdials", maxRetries))
  184.     if (killtime != "")
  185.         CHECK(sendLine("killtime", killtime))
  186.     /*
  187.      * If the dialstring is different from the
  188.      * displayable number then pass both.
  189.      */
  190.     if (destNumbers[i] != externalNumbers[i]) {
  191.         /*
  192.          * XXX
  193.          * We should bump the protocol version and
  194.          * warn the submitter if this isn't supported
  195.          * by the server.
  196.          */
  197.         CHECK(sendLine("external", externalNumbers[i]))
  198.     }
  199.     CHECK(sendLine("number", destNumbers[i]))
  200.     CHECK(sendLine("sender", senderName))
  201.     CHECK(sendLine("mailaddr", mailbox))
  202.     if (jobtag != "")
  203.         CHECK(sendLine("jobtag", jobtag))
  204.     CHECK(sendLine("resolution", (int) vres))
  205.     CHECK(sendLine("pagewidth", (int) pageWidth))
  206.     CHECK(sendLine("pagelength", (int) pageLength))
  207.     if (notify == when_done)
  208.         CHECK(sendLine("notify", "when done"))
  209.     else if (notify == when_requeued)
  210.         CHECK(sendLine("notify", "when requeued"))
  211.     if (coverSheet) {
  212.         FILE* fp = fopen(coverPages[i], "r");
  213.         if (fp != NULL) {
  214.         sendLine("cover", 1);    // cover sheet sub-protocol version 1
  215.         // copy prototype cover page
  216.         char line[1024];
  217.         while (fgets(line, sizeof (line)-1, fp)) {
  218.             char* cp = strchr(line, '\n');
  219.             if (cp)
  220.             *cp = '\0';
  221.             if (!sendCoverLine("%s", line))
  222.             break;
  223.         }
  224.         fclose(fp);
  225.         CHECK(sendLine("..\n"))
  226.         } else
  227.         printWarning(
  228.             "the cover page for \"%s\" disappeared; none was sent",
  229.             (char*) destNumbers[i]);
  230.     }
  231.     CHECK(sendLine("end", i))
  232.     }
  233.     return (sendLine(".\n"));
  234. }
  235. #undef CHECK
  236.  
  237. u_int SendFaxClient::getNumberOfDestinations() const
  238.    { return destNames.length(); }
  239. u_int SendFaxClient::getNumberOfFiles() const    { return files->length(); }
  240. u_int SendFaxClient::getTotalPages() const    { return totalPages; }
  241.  
  242. void SendFaxClient::setCoverComments(const char* s)    { comments = s; }
  243. const fxStr& SendFaxClient::getCoverComments() const    { return comments; }
  244. void SendFaxClient::setCoverRegarding(const char* s)    { regarding = s; }
  245. const fxStr& SendFaxClient::getCoverRegarding() const    { return regarding; }
  246. void SendFaxClient::setCoverSheet(fxBool b)        { coverSheet = b; }
  247. void SendFaxClient::setResolution(float r)        { vres = r; }
  248. void SendFaxClient::setPollRequest(fxBool b)        { pollCmd = b; }
  249. fxBool SendFaxClient::getPollRequest() const        { return pollCmd; }
  250. void SendFaxClient::setNotification(FaxNotify n)    { notify = n; }
  251. void SendFaxClient::setKillTime(const char* s)        { killtime = s; }
  252. void SendFaxClient::setSendTime(const char* s)        { sendtime = s; }
  253. void SendFaxClient::setFromIdentity(const char* s)    { from = s; }
  254. void SendFaxClient::setJobTag(const char* s)        { jobtag = s; }
  255. const fxStr& SendFaxClient::getFromIdentity() const    { return from; }
  256. void SendFaxClient::setMaxRetries(int n)        { maxRetries = n; }
  257. void SendFaxClient::setVerbose(fxBool b)        { verbose = b; }
  258. fxBool SendFaxClient::getVerbose() const        { return verbose; }
  259.  
  260. fxBool
  261. SendFaxClient::setPageSize(const char* name)
  262. {
  263.     PageSizeInfo* info = PageSizeInfo::getPageSizeByName(name);
  264.     if (info) {
  265.     pageWidth = info->width();
  266.     pageLength = info->height();
  267.     pageSize = name;
  268.     delete info;
  269.     return (TRUE);
  270.     } else {
  271.     printError("Unknown page size \"%s\"", name);
  272.     return (FALSE);
  273.     }
  274. }
  275. const fxStr& SendFaxClient::getPageSize() const    { return pageSize; }
  276. float SendFaxClient::getPageWidth() const    { return pageWidth; }
  277. float SendFaxClient::getPageLength() const    { return pageLength; }
  278.  
  279. /*
  280.  * Add a new destination name and number.
  281.  */
  282. fxBool
  283. SendFaxClient::addDestination(
  284.     const char* person,
  285.     const char* faxnum,
  286.     const char* company,
  287.     const char* location
  288. )
  289. {
  290.     if (!faxnum || faxnum[0] == '\0') {
  291.     printError("No fax number specified");
  292.     return (FALSE);
  293.     }
  294.     destNumbers.append(faxnum);
  295.     destNames.append(person ? person : "");
  296.     destLocations.append(location ? location : "");
  297.     destCompanys.append(company ? company : "");
  298.     return (TRUE);
  299. }
  300.  
  301. /*
  302.  * Add a new file to send to each destination.
  303.  */
  304. void
  305. SendFaxClient::addFile(const char* filename)
  306. {
  307.     u_int i = files->length();
  308.     files->resize(i+1);
  309.     (*files)[i].name = filename;
  310. }
  311.  
  312. /*
  313.  * Check parsed sender identity against identity obtained
  314.  * by looking at the real uid.  If they're different and
  315.  * we are not running as a "trusted user", abort.
  316.  */
  317. fxBool
  318. SendFaxClient::verifyPermission()
  319. {
  320.     uid_t uid = getuid();
  321.     if (uid == 0)
  322.     return (TRUE);
  323.     passwd* pwd = getpwuid(uid);
  324.     fxStr name(pwd ? pwd->pw_name : "nobody");
  325.     if (name == "daemon" || name == "uucp" || name == "fax")
  326.     return (TRUE);
  327.     // check specified address against real uid's address
  328.     fxStr user(mailbox.head(mailbox.next(0, '@')));
  329.     user.remove(0, user.nextR(user.length(), '!'));
  330.     if (user != getUserName()) {
  331.     printError(
  332.         "Unauthorized attempt to set sender's identity (from %s, uid %s)",
  333.         (char*) user, (char*) getUserName());
  334.     return (FALSE);
  335.     }
  336.     return (TRUE);
  337. }
  338.  
  339. /*
  340.  * Create the mail address for a local user.
  341.  */
  342. void
  343. SendFaxClient::setMailbox(const char* user)
  344. {
  345.     fxStr acct(user);
  346.     if (acct.next(0, '@') == acct.length()) {
  347.     char hostname[64];
  348.     (void) gethostname(hostname, sizeof (hostname));
  349.     struct hostent* hp = gethostbyname(hostname);
  350.     mailbox = acct | "@" | (hp ? hp->h_name : hostname);
  351.     } else
  352.     mailbox = acct;
  353. }
  354. const fxStr& SendFaxClient::getMailbox() const        { return mailbox; }
  355.  
  356. /*
  357.  * Setup the sender's identity.
  358.  */
  359. fxBool
  360. SendFaxClient::setupSenderIdentity(const fxStr& from)
  361. {
  362.     FaxClient::setupUserIdentity();        // client identity
  363.  
  364.     if (from != "") {
  365.     u_int l = from.next(0, '<');
  366.     if (l == from.length()) {
  367.         l = from.next(0, '(');
  368.         if (l != from.length()) {        // joe@foobar (Joe Schmo)
  369.         mailbox = from.head(l);
  370.         l++; senderName = from.token(l, ')');
  371.         } else {                // joe
  372.         setMailbox(from);
  373.         if (from == getUserName())
  374.             senderName = FaxClient::getSenderName();
  375.         else
  376.             senderName = "";
  377.         }
  378.     } else {                // Joe Schmo <joe@foobar>
  379.         senderName = from.head(l);
  380.         l++; mailbox = from.token(l, '>');
  381.     }
  382.     if (senderName == "" && mailbox != "") {
  383.         /*
  384.          * Mail address, but no "real name"; construct one from
  385.          * the account name.  Do this by first stripping anything
  386.          * to the right of an '@' and then stripping any leading
  387.          * uucp patch (host!host!...!user).
  388.          */
  389.         senderName = mailbox;
  390.         senderName.resize(senderName.next(0, '@'));
  391.         senderName.remove(0, senderName.nextR(senderName.length(), '!'));
  392.     }
  393.  
  394.     // strip and leading&trailing white space
  395.     senderName.remove(0, senderName.skip(0, " \t"));
  396.     senderName.resize(senderName.skipR(senderName.length(), " \t"));
  397.     mailbox.remove(0, mailbox.skip(0, " \t"));
  398.     mailbox.resize(mailbox.skipR(mailbox.length(), " \t"));
  399.     if (senderName == "" || mailbox == "") {
  400.         printError("Malformed (null) sender name or mail address");
  401.         return (FALSE);
  402.     }
  403.  
  404.     if (!verifyPermission())
  405.         return (FALSE);
  406.     } else {
  407.     senderName = FaxClient::getSenderName();
  408.     setMailbox(getUserName());
  409.     }
  410.     return (TRUE);
  411. }
  412. const fxStr& SendFaxClient::getSenderName() const    { return senderName; }
  413.  
  414. /*
  415.  * Process a file submitted for transmission.
  416.  */
  417. fxBool
  418. SendFaxClient::handleFile(FileInfo& info)
  419. {
  420.     info.rule = fileType(info.name);
  421.     if (!info.rule)
  422.     return (FALSE);
  423.     if (info.rule->getCmd() != "") {    // conversion required
  424.     fxStr temp(_PATH_TMP "faxsndXXXXXX");
  425.     tempFiles.append(temp);
  426.     mktemp((char*) temp);
  427.     fxStr sysCmd = info.rule->getFmtdCmd(info.name, temp,
  428.         hres, vres, "1", pageSize);
  429.     if (verbose)
  430.         printf("CONVERT \"%s\"\n", (char*) sysCmd);
  431.     if (system((char*) sysCmd) != 0) {
  432.         unlink((char*) temp);
  433.         u_int ix = tempFiles.find(temp);
  434.         if (ix != fx_invalidArrayIndex)
  435.         tempFiles.remove(ix);
  436.         printError("Error converting data; command was \"%s\"",
  437.         (char*) sysCmd);
  438.         return (FALSE);
  439.     }
  440.     info.name = temp;
  441.     info.isTemp = TRUE;
  442.     } else                // already postscript or tiff
  443.     info.isTemp = FALSE;
  444.     switch (info.rule->getResult()) {
  445.     case TypeRule::TIFF:
  446.     countTIFFPages(info.name);
  447.     break;
  448.     case TypeRule::POSTSCRIPT:
  449.     estimatePostScriptPages(info.name);
  450.     break;
  451.     }
  452.     return (TRUE);
  453. }
  454.  
  455. /*
  456.  * Return a TypeRule for the specified file.
  457.  */
  458. const TypeRule*
  459. SendFaxClient::fileType(const char* filename)
  460. {
  461.     struct stat sb;
  462.     int fd = ::open(filename, O_RDONLY);
  463.     if (fd < 0) {
  464.     printError("%s: Can not open file", filename);
  465.     return (NULL);
  466.     }
  467.     if (fstat(fd, &sb) < 0) {
  468.     printError("%s: Can not stat file", filename);
  469.     ::close(fd);
  470.     return (NULL);
  471.     }
  472.     if ((sb.st_mode & S_IFMT) != S_IFREG) {
  473.     printError("%s: Not a regular file", filename);
  474.     ::close(fd);
  475.     return (NULL);
  476.     }
  477.     char buf[512];
  478.     int cc = read(fd, buf, sizeof (buf));
  479.     ::close(fd);
  480.     if (cc == 0) {
  481.     printError("%s: Empty file", filename);
  482.     return (NULL);
  483.     }
  484.     const TypeRule* tr = typeRules->match(buf, cc);
  485.     if (!tr) {
  486.     printError("%s: Can not determine file type", filename);
  487.     return (NULL);
  488.     }
  489.     if (tr->getResult() == TypeRule::ERROR) {
  490.     fxStr emsg(tr->getErrMsg());
  491.     printError("%s: %s", filename, (char*) emsg);
  492.     return (NULL);
  493.     }
  494.     return tr;   
  495. }
  496.  
  497. #include "tiffio.h"
  498.  
  499. /*
  500.  * Count the number of ``pages'' in a TIFF file.
  501.  */
  502. void
  503. SendFaxClient::countTIFFPages(const char* filename)
  504. {
  505.     TIFF* tif = TIFFOpen(filename, "r");
  506.     if (tif) {
  507.     do {
  508.         totalPages++;
  509.     } while (TIFFReadDirectory(tif));
  510.     TIFFClose(tif);
  511.     }
  512. }
  513.  
  514. /*
  515.  * Count the number of pages in a PostScript file.
  516.  * We can really only estimate the number as we
  517.  * depend on the DSC comments to figure this out.
  518.  */
  519. void
  520. SendFaxClient::estimatePostScriptPages(const char* filename)
  521. {
  522.     FILE* fd = fopen(filename, "r");
  523.     if (fd != NULL) {
  524.     char line[2048];
  525.     if (fgets(line, sizeof (line)-1, fd) != NULL) {
  526.         /*
  527.          * We only consider ``conforming'' PostScript documents.
  528.          */
  529.         if (line[0] == '%' && line[1] == '!') {
  530.         int npagecom = 0;    // # %%Page comments
  531.         int npages = 0;        // # pages according to %%Pages comments
  532.         while (fgets(line, sizeof (line)-1, fd) != NULL) {
  533.             int n;
  534.             if (strncmp(line, "%%Page:", 7) == 0)
  535.             npagecom++;
  536.             else if (sscanf(line, "%%%%Pages: %u", &n) == 1)
  537.             npages += n;
  538.         }
  539.         /*
  540.          * Believe %%Pages comments over counting of %%Page comments.
  541.          */
  542.         if (npages > 0)
  543.             totalPages += npages;
  544.         else if (npagecom > 0)
  545.             totalPages += npagecom;
  546.         }
  547.     }
  548.     fclose(fd);
  549.     }
  550. }
  551.  
  552. /*
  553.  * Invoke the cover page generation program.
  554.  */
  555. fxStr
  556. SendFaxClient::makeCoverPage(
  557.     const fxStr& name,
  558.     const fxStr& number,
  559.     const fxStr& company,
  560.     const fxStr& location,
  561.     const fxStr& sender
  562. )
  563. {
  564.     fxStr templ(_PATH_TMP "sndfaxXXXXXX");
  565.     tempFiles.append(templ);
  566.     int fd = mkstemp((char*) templ);
  567.     if (fd >= 0) {
  568.     fxStr cmd("faxcover");
  569.     if (getPageSize() != "default")
  570.         cmd.append(" -s " | getPageSize());
  571.     cmd.append(" -n \"" | number | "\"");
  572.     cmd.append(" -t \"" | name | "\"");
  573.     cmd.append(" -f \"" | sender | "\"");
  574.     if (getCoverComments() != "") {
  575.         cmd.append(" -c \"");
  576.         cmd.append(getCoverComments());
  577.         cmd.append("\"");
  578.     }
  579.     if (getCoverRegarding() != "") {
  580.         cmd.append(" -r \"");
  581.         cmd.append(getCoverRegarding());
  582.         cmd.append("\"");
  583.     }
  584.     if (location != "") {
  585.         cmd.append(" -l \"");
  586.         cmd.append(location);
  587.         cmd.append("\"");
  588.     }
  589.     if (company != "") {
  590.         cmd.append(" -x \"");
  591.         cmd.append(company);
  592.         cmd.append("\"");
  593.     }
  594.     if (getTotalPages() > 0) {
  595.         fxStr pages((int) getTotalPages(), "%u");
  596.         cmd.append(" -p " | pages);
  597.     }
  598.     if (getVerbose())
  599.         printf("COVER SHEET \"%s\"\n", (char*) cmd);
  600.     FILE* fp = popen(cmd, "r");
  601.     if (fp != NULL) {
  602.         // copy prototype cover page
  603.         char line[1024];
  604.         while (fgets(line, sizeof (line)-1, fp))
  605.         (void) write(fd, line, strlen(line));
  606.         if (pclose(fp) == 0) {
  607.         ::close(fd);
  608.         return (templ);
  609.         }
  610.     }
  611.     printError("Error creating cover sheet; command was \"%s\"",
  612.         (char*) cmd);
  613.     } else
  614.     printError("%s: Can not create temporary file for cover sheet",
  615.         (char*) templ);
  616.     unlink((char*) templ);
  617.     templ = "";
  618.     return (templ);
  619. }
  620.  
  621. fxBool
  622. SendFaxClient::sendCoverLine(const char* va_alist ...)
  623. #define    fmt va_alist
  624. {
  625.     va_list ap;
  626.     va_start(ap, va_alist);
  627.     char buf[4096];
  628.     vsprintf(buf, fmt, ap);
  629.     va_end(ap);
  630.     return sendLine("!", buf);
  631. }
  632. #undef fmt
  633.  
  634. #define    isCmd(s)    (strcasecmp(s, cmd) == 0)
  635.  
  636. void
  637. SendFaxClient::recvConf(const char* cmd, const char* tag)
  638. {
  639.     if (isCmd("permission")) {
  640.     gotPermission = TRUE;
  641.     permission = (strcasecmp(tag, "granted") == 0);
  642.     } else
  643.     printError("Unknown status message received: \"%s:%s\"", cmd, tag);
  644. }
  645.  
  646. void
  647. SendFaxClient::recvEof()
  648. {
  649.     stopRunning();
  650. }
  651.  
  652. void
  653. SendFaxClient::recvError(const int err)
  654. {
  655.     printError("Socket read error: %s", strerror(err));
  656.     stopRunning();
  657. }
  658.  
  659. FileInfo::FileInfo()
  660. {
  661.     isTemp = FALSE;
  662.     tagLine = TRUE;
  663.     rule = NULL;
  664. }
  665. FileInfo::~FileInfo()
  666. {
  667.     if (isTemp)
  668.     unlink((char*) name);
  669. }
  670. fxIMPLEMENT_ObjArray(FileInfoArray, FileInfo);
  671.